package cn.meye11.crm.common;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

/**
 * Created by Adam.yao on 2017/10/25.
 */
public class DateUtils {
    /**
     * //====================== 日期格式化常量 =====================//
     */
    public static final String FORMAT_PATTERN_HOUR_MINUTE = "HH:mm";
    public static final String FORMAT_PATTERN_DATE_MINUTE = "yyyy-MM-dd HH:mm";

    public static final String FORMAT_PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String FORMAT_PATTERN_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss";
    public static final String FORMAT_PATTERN_DATETIME_ZH_CN = "yyyy年MM月dd日 HH时mm分";
    public static final String FORMAT_PATTERN_DATE = "yyyy-MM-dd";
    public static final String FORMAT_PATTERN_YEAR_MONTH = "yyyy-MM";
    public static final String FORMAT_PATTERN_YEAR = "yyyy";
    public static final String FORMAT_PATTERN_DATETIME_SERIAL = "yyyyMMddHHmmss";
    public static final String FORMAT_PATTERN_DATETIME_HOUR = "yyyyMMddHH";
    public static final String FORMAT_PATTERN_DATE_SERIAL = "yyyyMMdd";
    public static final String FORMAT_PATTERN_YEAR_MONTH_SERIAL = "yyyyMM";
    public static final String FORMAT_PATTERN_MONTH_DATE_SERIAL = "d/M";

    public static final long ONE_SECOND = 1000L;
    public static final long ONE_MINUTE = 60 * ONE_SECOND;
    public static final long ONE_HOUR = 60 * ONE_MINUTE;
    public static final long ONE_DAY = 24 * ONE_HOUR;

    public static String dateToStr(Date date) {

        return dateToStr(date, FORMAT_PATTERN_DATETIME);
    }

    public static String dateToStr(Date date, String pattern) {

        if (null == date) {
            return null;
        }
        return new SimpleDateFormat(pattern, Locale.getDefault()).format(date);
    }

    public static String localDateTimeToStr(LocalDateTime dateTime) {
        return DateTimeFormatter.ofPattern(FORMAT_PATTERN_DATETIME).format(dateTime);
    }

    public static String localDateTimeToStr(LocalDateTime dateTime, String format) {
        return DateTimeFormatter.ofPattern(format).format(dateTime);
    }

    public static Date strToDate(String str) {

        return strToDate(str, FORMAT_PATTERN_DATETIME);
    }

    public static LocalDateTime strToLocalDateTime(String str) {
        if (str == null) {
            return null;
        }

        DateTimeFormatter df = DateTimeFormatter.ofPattern(FORMAT_PATTERN_DATETIME);
        return LocalDateTime.parse(str, df);
    }

    /**
     * 字符转成日期 yyyy-MM-dd
     *
     * @param str
     * @return
     */
    public static Date strToDateDay(String str) {

        return strToDate(str, FORMAT_PATTERN_DATE);
    }

    /**
     * 字符转成日期 yyyy-MM-dd HH MM SS
     */
    public static Date strToDateDayTime(String str) {

        return strToDate(str, FORMAT_PATTERN_DATETIME);
    }


    public static Date strToDate(String str, String pattern) {

        if (str == null) {
            return null;
        }

        Date date = null;
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);

        try {
            date = sdf.parse(str);
        } catch (ParseException e) {
            Meiye11ExceptionUtils.warn(e, str, pattern);
            throw new BaseException("时间格式化错误，无法将：" + str + "格式化为：" + pattern);
        }

        return date;
    }

    @Deprecated
    public static Date getDateFromString(String str) throws ParseException {

        return strToDate(str);
    }

    public static Date addYear(int num) {

        return addYear(new Date(), num);
    }

    public static Date addYear(Date date, int num) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + num);

        return calendar.getTime();
    }

    public static Date addDays(Date date, int num) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_YEAR, num);

        return calendar.getTime();
    }
    public static Date addHours(Date date, int num) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, num);

        return calendar.getTime();
    }

    /**
     * 传入日期，返回cron表达式
     *
     * @param date
     * @return
     */
//    public static String dateToCron(Date date) {
//        if (date == null) {
//            throw new BaseException("日期不可为空");
//        }
//        cal.setTime(date);
//        //年
//        int year = cal.get(Calendar.YEAR);
//        //月
//        int month = cal.get(Calendar.MONTH) + 1;
//        //日
//        int day = cal.get(Calendar.DAY_OF_MONTH);
//        //时
//        int hour = cal.get(Calendar.HOUR_OF_DAY);
//        //分
//        int minute = cal.get(Calendar.MINUTE);
//        //秒
//        int second = cal.get(Calendar.SECOND);
//        return " " + second + " " + minute + " " + hour + " " + day + " " + month + " ? " + year + " ";
//    }

    /**
     * 日期转Cron表达式
     *
     * @param dateTime
     * @return
     */
    public static String toCron(LocalDateTime dateTime) {
        return DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy").format(dateTime);
    }

    /**
     * Date转LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static int differentDays(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new BaseException("日期为空时不可获取日期相差天数");
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);

        // 同一年
        if (year1 != year2) {
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                // 闰年
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
                    timeDistance += 366;
                    // 不是闰年
                } else {
                    timeDistance += 365;
                }
            }

            return timeDistance + (day2 - day1);
        } else    //不同年
        {
            return day2 - day1;
        }
    }

    public static String getPastDate(int past, String pattern) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - past);
        Date today = calendar.getTime();
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        return format.format(today);
    }

    /**
     * 范围年
     *
     * @param date
     * @return
     */
    public static Date[] rangeYear(Date date) {

        Date[] ret = new Date[2];
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), 0, 1, 0, 0, 0);
        ret[0] = calendar.getTime();
        calendar.set(calendar.get(Calendar.YEAR), 11, 31, 23, 59, 59);
        ret[1] = calendar.getTime();

        return ret;
    }

    /**
     * 范围月
     *
     * @param date
     * @return
     */
    public static Date[] rangeMonth(Date date) {

        Date[] ret = new Date[2];
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), 1, 0, 0, 0);
        ret[0] = calendar.getTime();
        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.SECOND, -1);
        ret[1] = calendar.getTime();

        return ret;
    }

    /**
     * 范围周
     *
     * @param date
     * @return
     */
    public static Date[] rangeWeek(Date date) {

        Date[] ret = new Date[2];
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONDAY), calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        ret[0] = calendar.getTime();
        calendar.add(Calendar.WEEK_OF_MONTH, 1);
        calendar.add(Calendar.SECOND, -1);
        ret[1] = calendar.getTime();

        return ret;
    }

    /**
     * 范围日
     *
     * @param date
     * @return
     */
    public static Date[] rangeDay(Date date) {

        Date[] ret = new Date[2];
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE), 0, 0, 0);
        ret[0] = calendar.getTime();

        //防止时间戳出现毫秒级乃至秒级误差 引起搜索问题
        long time = ret[0].getTime();
        time = time / 10000 * 10000;
        Date datetemp1 = new Date(time);
        ret[0] = datetemp1;

        calendar.add(Calendar.DATE, 1);
        calendar.add(Calendar.MILLISECOND, -1);
        ret[1] = calendar.getTime();

        long time2 = ret[1].getTime();
        time2 = time2 / 1000 * 1000;
        Date datetemp2 = new Date(time2);
        ret[1] = datetemp2;

        return ret;
    }

    /**
     * 天列表
     *
     * @param begin
     * @param end
     * @return
     */
    public static List<Date> dayList(Date begin, Date end) {

        List<Date[]> dates = dayRangeList(begin, end);


        List<Date> result = new ArrayList<>();

        if (null != dates && !dates.isEmpty()) {
            dates.forEach(e -> {
                result.add(null != e && null != e[0] ? e[0] : null);
            });
        }

        return null != result && !result.isEmpty() ? result : null;
    }

    /**
     * 天范围列表
     *
     * @param begin
     * @param end
     * @return
     */
    public static List<Date[]> dayRangeList(Date begin, Date end) {

        Calendar calendarBegin = Calendar.getInstance();
        calendarBegin.setTime(begin);
        calendarBegin.set(calendarBegin.get(Calendar.YEAR), calendarBegin.get(Calendar.MONTH), calendarBegin.get(Calendar.DATE), 0, 0, 0);
        Calendar calendarEnd = Calendar.getInstance();
        calendarEnd.setTime(end);
        calendarEnd.set(calendarEnd.get(Calendar.YEAR), calendarEnd.get(Calendar.MONTH), calendarEnd.get(Calendar.DATE), 0, 0, 0);
        List<Date[]> result = new ArrayList<>();

        if (calendarBegin.getTime().equals(calendarEnd.getTime())) {
            result.add(rangeDay(calendarBegin.getTime()));
        } else {
            result.add(rangeDay(calendarBegin.getTime()));

            while (calendarEnd.after(calendarBegin)) {
                calendarBegin.add(Calendar.DATE, 1);
                result.add(rangeDay(calendarBegin.getTime()));
            }
        }
        return result;
    }

    public static Date mongoDate(Date date) {
        String dateStr = dateToStr(date);
        SimpleDateFormat format =
                new SimpleDateFormat(FORMAT_PATTERN_DATETIME);
        format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT")));
        Date d = null;
        try {
            d = format.parse(dateStr);
        } catch (ParseException e) {
            Meiye11ExceptionUtils.error(e);
        }
        return d;
    }


    public static Date yesterday(Date today) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(today);
        calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) - 1);
        return calendar.getTime();
    }


    public static Date tomorro(Date today) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(today);
        calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + 1);
        return calendar.getTime();
    }


    //幾天後

    public static Date passNDay(Date day, int nday) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(day);
        calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + nday);
        return calendar.getTime();
    }

    //幾天前

    public static Date beforeNDay(Date day, int nday) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(day);
        calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) - nday);
        return calendar.getTime();
    }

    /**
     * 描述:获取下一个月的第一天.
     *
     * @return
     */
    public static String getPerFirstDayOfMonth() {
        SimpleDateFormat dft = new SimpleDateFormat(FORMAT_PATTERN_DATE);
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 描述:获取月的最后一天.
     *
     * @param day
     * @param amount 月份便宜（上个月-1，本月0，下个月1）
     * @return
     */
    public static String getLastMaxMonthDate(Date day, int amount) {
        SimpleDateFormat dft = new SimpleDateFormat(FORMAT_PATTERN_DATE);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(day);
        calendar.add(Calendar.MONTH, amount);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 获得入参日期后next月的第一天
     *
     * @param date
     * @param next
     * @return
     */
    public static Date firstDayOfNextMonth(Date date, int next) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, next);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }


    public static void main(String[] args) throws ParseException {

//        SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_PATTERN_DATETIME);
//        String startTimeStr = DateUtils.dateToStr(new Date());
//        Date date = DateUtils.strToDate(startTimeStr, DateUtils.FORMAT_PATTERN_DATE);

//        Long l = sdf.parse("2018-5-7 16:57:33").getTime()-sdf.parse("2018-5-1 16:47:33").getTime();
//        Date date = sdf.parse("2019-06-15 00:00:00");
//        Date date2 = sdf.parse("2019-06-18 00:00:00");

//        System.out.println(l);
//        Date[] x = rangeDay(date);
//        System.out.println(dateToStr(x[0]));
//        System.out.println(dateToStr(x[1]));
//        List<Date[]> dates = DateUtils.dayRangeList(date, date2);
//        dates.forEach(e -> {
//            System.out.println(dateToStr(e[0]));
//            System.out.println(dateToStr(e[1]));
//        });
//        Date[] dates1 = rangeDay(date);
//        System.out.println(dateToStr(dates1[0]));
//        System.out.println(dateToStr(dates1[1]));

//        TreeSet<String> strings = new TreeSet<>();
//        strings.add("2019-06-15");
//        strings.add("2019-06-15");
//        strings.add("2019-06-15");
//        strings.forEach(s -> {
//            System.out.println(s);
//        });
        /*Date yesterday = yesterday(new Date());
        String dateStr = dateToStr(yesterday, FORMAT_PATTERN_DATE);
        System.out.println(dateToStr(yesterday));
        Date date = strToDate(dateStr, FORMAT_PATTERN_DATE);
        System.out.println(dateToStr(date));
        Date[] dates = rangeDay(date);
        for (Date d : dates) {
            System.out.println(dateToStr(d));
        }*/
//        Date now = new Date();
//        Date[] dates2 = rangeDay(now);
//        for (Date dates : dates2) {
//            System.out.println(dateToStr(date));
//        }
//        System.out.println(Boolean.parseBoolean("false"));
    }

    /**
     * LocalDateTime 转 String
     *
     * @param dateTime
     * @return
     */
    public static String print(LocalDateTime dateTime) {
        return DateTimeFormatter.ofPattern(FORMAT_PATTERN_DATETIME).format(dateTime);
    }

    /**
     * LocalDateTime 转 Date
     *
     * @param dateTime
     * @return
     */
    public static Date LocalDateTimeToDate(LocalDateTime dateTime) {
        if (null == dateTime) {
            return null;
        }
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = dateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * LocalDate 转 Date
     *
     * @param localDate
     * @return
     */
    public static Date LocalDateToDate(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
        return Date.from(zonedDateTime.toInstant());
    }

    /**
     * 获取当
     *
     * @author zlh
     * @date 2021年05月28日 17:32
     */
    public static LocalDateTime getStartOrEndDayOfDay(LocalDate today, Boolean isFirst) {
        LocalDate resDate = LocalDate.now();
        if (today == null) {
            today = resDate;
        }
        if (isFirst) {
            return LocalDateTime.of(today, LocalTime.MIN);
        } else {
            return LocalDateTime.of(today, LocalTime.MAX);
        }
    }

    /**
     * 计算2个日期之间相差的  相差多少年月日
     * 比如：2011-02-02 到  2017-03-02 相差 6年，1个月，0天
     *
     * @param fromDate
     * @param toDate
     * @return
     */
    public static DayCompare dayComparePrecise(Date fromDate, Date toDate) {
        if (null == fromDate || null == toDate) {
            return null;
        }
        Calendar from = Calendar.getInstance();
        from.setTime(fromDate);
        Calendar to = Calendar.getInstance();
        to.setTime(toDate);

        int fromYear = from.get(Calendar.YEAR);
        int fromMonth = from.get(Calendar.MONTH);
        int fromDay = from.get(Calendar.DAY_OF_MONTH);

        int toYear = to.get(Calendar.YEAR);
        int toMonth = to.get(Calendar.MONTH);
        int toDay = to.get(Calendar.DAY_OF_MONTH);
        int year = toYear - fromYear;
        int month = toMonth - fromMonth;
        int day = toDay - fromDay;
        return DayCompare.builder().day(day).month(month).year(year).build();
    }

    /**
     * 获取当前月最后一天
     *
     * @return
     */
    public static LocalDateTime getLastDayOfMonth() {
        return LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59);
    }

    /**
     * 获取当前月第一天
     *
     * @return
     */
    public static LocalDateTime getFirstDayOfMonth() {
        return LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0);
    }

    /**
     * 获取月最后一天
     *
     * @return
     */
    public static LocalDateTime getLastDayOfMonth(LocalDateTime dateTime) {
        return dateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59);
    }

    public static ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        mapper.registerModule(javaTimeModule);
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
    }


    /**
     * 验证字符串是否为指定的日期格式
     */
    public static boolean isValidDateFormat(String input, String format) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        dateFormat.setLenient(false);

        try {
            dateFormat.parse(input);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }
}
